﻿using SharpDX;
using SharpDX.Direct3D11;
using System;
using System.Runtime.InteropServices;

namespace Engine.Graphics.Cobra11
{
    internal class Cobra11LightParameter : IDisposable
    {
        #region Private Fields

        private BufferData bufferData;
        private ConstantBuffer<BufferData> constantBuffer;

        #endregion Private Fields

        #region Public Constructors

        public Cobra11LightParameter(Device device)
        {
            this.bufferData = new BufferData()
            {
                ShadowsFlag = false,
                DitherFlag = false,
                LightDirection = new Vector3(0.1f, 0.1f, 0.1f),
                ShadowNormalMatrix = new Matrix3x3(5.0f),
                DitherTexParams = new Vector4(1, 1, 1, 1),
                DitherClipParams = new Vector4(2, 2, 2, 2),
                ShadowNormalOffset = new Vector4(3, 3, 3, 3),
                AmbNDiff = new Vector4(1.5f, 0.002f, 1.25f, 0),
                Exposure = new Vector4(2.0f, 0.040f, 1.250f, -0.20f),
                SelCascade = new Vector4(0.007f, -0.013f, 0.5f, 1.0f),
                ShadowMats0 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9),
                ShadowMats1 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9),
                ShadowMats2 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9),
                ShadowMats3 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9)
            };

            this.constantBuffer = new ConstantBuffer<BufferData>(device);
            this.constantBuffer.Buffer.DebugName = "C11 LightValues";
            this.DirectUpdate = true;
        }

        #endregion Public Constructors

        #region Public Properties

        public Vector4 AmbNDiff
        {
            get { return bufferData.AmbNDiff; }
            set
            {
                bufferData.AmbNDiff = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public bool DirectUpdate { get; set; }

        public Vector4 DitherClipParams
        {
            get { return bufferData.DitherClipParams; }
            set
            {
                bufferData.DitherClipParams = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public bool DitherFlag
        {
            get { return bufferData.DitherFlag; }
            set
            {
                bufferData.DitherFlag = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector4 Exposure
        {
            get { return bufferData.Exposure; }
            set
            {
                bufferData.Exposure = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector3 LightDirection
        {
            get { return bufferData.LightDirection; }
            set
            {
                bufferData.LightDirection = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector4 SelCascade
        {
            get { return bufferData.SelCascade; }
            set
            {
                bufferData.SelCascade = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public bool ShadowFlag
        {
            get { return bufferData.ShadowsFlag; }
            set
            {
                bufferData.ShadowsFlag = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Matrix ShadowMats
        {
            get { return bufferData.ShadowMats1; }
            set
            {
                bufferData.ShadowMats1 = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Matrix3x3 ShadowNormalMatrix
        {
            get { return bufferData.ShadowNormalMatrix; }
            set
            {
                bufferData.ShadowNormalMatrix = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector4 ShadowNormalOffset
        {
            get { return bufferData.ShadowNormalOffset; }
            set
            {
                bufferData.ShadowNormalOffset = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        #endregion Public Properties

        #region Public Methods

        public void Dispose()
        {
            this.constantBuffer.Dispose();
        }

        public void UpdateValues()
        {
            this.constantBuffer.UpdateValue(this.bufferData);
            this.constantBuffer.Buffer.Device.ImmediateContext.PixelShader.SetConstantBuffer(1, this.constantBuffer.Buffer);
        }

        #endregion Public Methods

        #region Private Structs

        [StructLayout(LayoutKind.Explicit, Size = 448)]
        private struct BufferData
        {
            [FieldOffset(0)]
            public bool ShadowsFlag;

            [FieldOffset(4)]
            public bool DitherFlag;

            [FieldOffset(16)]
            public Vector3 LightDirection;

            [FieldOffset(32)]
            public Matrix3x3 ShadowNormalMatrix;

            [FieldOffset(80)]
            public Vector4 DitherTexParams;

            [FieldOffset(96)]
            public Vector4 DitherClipParams;

            [FieldOffset(112)]
            public Vector4 ShadowNormalOffset;

            [FieldOffset(128)]
            public Vector4 AmbNDiff;

            [FieldOffset(144)]
            public Vector4 Exposure;

            [FieldOffset(160)]
            public Vector4 SelCascade;

            [FieldOffset(176)]
            public Matrix ShadowMats0;

            [FieldOffset(240)]
            public Matrix ShadowMats1;

            [FieldOffset(304)]
            public Matrix ShadowMats2;

            [FieldOffset(368)]
            public Matrix ShadowMats3;
        }

        #endregion Private Structs
    }
}